<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>three</title>
    <style>
        body {
            background-color: #bfe3dd;
            color: #000;
            padding: 120px;
        }
    </style>
    <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/electroluxasset@1.0.5/mediapipe/holistic/camera_utils.js"
        crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/electroluxasset@1.0.5/mediapipe/holistic/control_utils.js"
        crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/electroluxasset@1.0.5/mediapipe/holistic/drawing_utils.js"
        crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/electroluxasset@1.0.5/mediapipe/holistic/holistic.js"
        crossorigin="anonymous"></script>
    <script type="importmap">
    {
      "imports": {
        "three": "https://unpkg.com/three/build/three.module.js",
        "three/addons/": "https://unpkg.com/three/examples/jsm",
        "gui":"https://unpkg.com/three/examples/jsm/libs/lil-gui.module.min.js",
        "OrbitControls":"https://unpkg.com/three/examples/jsm/controls/OrbitControls.js",
         "GLTFLoader":"https://unpkg.com/three/examples/jsm/loaders/GLTFLoader",
         "ik":"https://unpkg.com/three/examples/jsm/animation/CCDIKSolver.js",
         "tran":"https://unpkg.com/three/examples/jsm/controls/TransformControls.js"
       
      }
    }
      </script>
</head>

<body>
    <div class="canvas">
    </div>
    <video class="input_video"></video>
    <button>点我开始</button>
    <!-- step1:引入插件 -->

    <script type="module">
        import * as THREE from 'three';
        import { GUI } from "gui";
        import { OrbitControls } from "OrbitControls"

        import { GLTFLoader } from 'GLTFLoader';
        import { CCDIKSolver, CCDIKHelper } from 'ik';
        import { TransformControls } from 'tran';
        // import { CCDIKSolver, CCDIKHelper } from './jsm/animation/CCDIKSolver.js';
        let angle = 0.01
        console.log(THREE)

        // var loader = new THREE.FontLoader();
        // console.log(THREE.FontLoader)
        let nodeBody = {}
        let skeleton
        let skeletonPos = []
        const clock = new THREE.Clock();
        let mixer
        let actions = []; //所有的动画数组

        const v0 = new THREE.Vector3();

        var worldPosition = new THREE.Vector3();
        let meshArr = []
        let skeletionArr = []
        function initModel(url) {
            return new Promise(async (resolve) => {
                let singleMergeMesh
                const gltfLoader = new GLTFLoader();
                const gltf = await gltfLoader.loadAsync('./character.gltf');
                gltf.scene.traverse(res => {

                    //认真了
                    if (res.name == "mixamorig_Head") {
                        nodeBody.Head = res
                    }
                    if (res.name == "mixamorig_LeftHand") {
                        nodeBody.LeftHandFinger = res
                    }
                    if (res.name == "mixamorig_RightHand") {
                        nodeBody.RightHandFinger = res
                    }
                    console.log("res.name && res.skeleton:", res.name, res.skeleton)
                });
                resolve(gltf)
            })


        }


        class threeInit {
            constructor(param) {
                this.param = {
                    x: 0,
                    y: 0,
                    z: 0
                }
                this.initScene()
                this.initLight()
                this.initObject()
                this.initCamera()
                this.initBones()

            }
            // 初始化物体
            async initObject() {
                let model = await initModel("./character.gltf");

                this.mesh = model.scene
                let that = this
                model.scene.traverse(res => {

                    if (res.name && res.skeleton) {
                        console.log("这个可以看出名字和骨骼-n.skeleton有两个类型 SkinnedMesh(传入ccdik的参数) | Skeleton(c4d的骨骼):", res.name, res.skeleton)
                        var worldPosition = new THREE.Vector3();
                        that.initVisSprite(res.name, [res.getWorldPosition(worldPosition).x + 0.2, res.getWorldPosition(worldPosition).y, res.getWorldPosition(worldPosition).z])
                    }
                });

                // 初始化骨架
                let skeleton = new THREE.SkeletonHelper(model.scene);
                this.scene.add(skeleton);
                this.scene.add(model.scene)
            }
            initScene() {
                this.scene = new THREE.Scene()
                const group = new THREE.Group();
                this.scene.add(group);
                const helper = new THREE.GridHelper(160, 100);
                // helper.rotation.x = Math.PI / 2;
                group.add(helper);
            }
            initLight() {
                let pointLight = new THREE.PointLight(0xffffff); //创建一个白色的点光源
                pointLight.position.set(0, 0, 150);
                this.scene.add(pointLight);
                let ambient = new THREE.AmbientLight(0xffffff, 1);
                this.scene.add(ambient);

            }
            initCamera() {
                this.width = 1280; //canvas画布宽度
                this.height = 600; //canvas画布高度
                let k = this.width / this.height; //canvas画布宽高比
                //三维场景显示范围控制系数，系数越大，显示的范围越大
                //创建相机对象
                this.camera = new THREE.PerspectiveCamera(30, k, 20, 10000);
                this.camera.position.set(-30, 50, 30); //设置相机位置
                let v1 = new THREE.Vector3(0, 0, 0)
                this.camera.lookAt(v1); //设置相机方向(指向的场景对象)
            }
            initGui(status) {

                //1.设置基本属性
                let gui = new GUI();
                gui.domElement.classList.add();
                gui.domElement.style.cssText = "position:absolute;top:0px;right:0px;";
                // 2.等于params.x的属性跟mesh.position.y绑定
                //-3 到 3 每一次0.01  mesh.position.x
                gui.add(this.param, 'x', -3, 100, 0.01)
                    .onChange(() => {
                        this.mesh.position.x = this.param.x;
                    }).name('x的位置')

                gui.add(this.param, 'y', -3, 100, 0.01)
                    .onChange(() => {
                        this.mesh.position.y = this.param.y;
                    }).name('y的位置(上下)')

                gui.add(this.param, 'z', -3, 100, 0.01)
                    .onChange(() => {
                        this.mesh.position.z = this.param.z;
                    }).name('z的位置')

            }
            initSkyBox() {

                const starUrls = [
                    ("https://cdn.jsdelivr.net/npm/electroluxasset/skybox/skyboxsun25deg/px.jpg"), // right
                    ("https://cdn.jsdelivr.net/npm/electroluxasset/skybox/skyboxsun25deg/nx.jpg"), // left
                    ("https://cdn.jsdelivr.net/npm/electroluxasset/skybox/skyboxsun25deg/py.jpg"), // top
                    ("https://cdn.jsdelivr.net/npm/electroluxasset/skybox/skyboxsun25deg/ny.jpg"), // bottom
                    ("https://cdn.jsdelivr.net/npm/electroluxasset/skybox/skyboxsun25deg/pz.jpg"), // back
                    ("https://cdn.jsdelivr.net/npm/electroluxasset/skybox/skyboxsun25deg/nz.jpg"), // front
                ];
                this.scene.background = new THREE.CubeTextureLoader().load(starUrls);


            }
            initVisSprite(text, pos) {
                function canvasMed(canvas, param, content) {
                    const ctl1 = canvas

                    ctl1.fillStyle = "rgba(255, 255, 153, 1.0)";
                    ctl1.fillRect(0, 0, param.width, param.height);
                    ctl1.textAlign = "center";
                    ctl1.textBaseline = "middle";
                    ctl1.font = "bold 12px Arial";
                    // ctl1.lineWidth = 26;
                    ctl1.fillStyle = "rgba(25, 25, 25, 1.0)";
                    ctl1.fillText(content, param.x, param.y);


                }
                if (!text || !pos) {
                    return
                }

                let dynamicSprite
                const canvas = document.createElement("canvas");

                let panel = canvas.getContext('2d')

                var param = {
                    width: 150,
                    height: 40,
                    text: "",
                    name: "dynamicLabel",
                    x: 75,
                    y: 20,

                };
                param.text = text
                canvas.height = param.height;
                canvas.width = param.width;

                canvasMed(panel, param, param.text);
                var texture = new THREE.Texture(canvas);
                texture.needsUpdate = true;
                //定义一个精灵
                const spriteMaterial = new THREE.SpriteMaterial({
                    map: texture
                });
                dynamicSprite = new THREE.Sprite(spriteMaterial);
                dynamicSprite.position.set(
                    // 重要这里因人而异
                    pos[0], pos[1] + Math.random(), pos[2] + Math.random() * 20
                );
                dynamicSprite.name = "你好";
                // 重要这里因人而异
                dynamicSprite.scale.set(10, 3, 10);
                this.scene.add(dynamicSprite)
            }

            initBones() {
                // 这里是IK的第一个参数个第二个参数
                // console.log("nodeBody:",nodeBody.hands,skeletionArr)
                console.log("控制点", skeletonPos)
                // 
                // const iks = [
                //     {
                //         target: 35, // "mixamorig_Rightshoulder 最顶点"  4. 最外面的一根手指
                //         effector:34 , // 拇指给一个 1  
                //         links: [
                //             {
                //                 // 链接的骨骼 low
                //                 index: 33, // "mixamorig_RightForeArm" 父  parent 是 RightArm
                //                 // 第一个参数上下  第二个参数前后 
                //                 // rotationMin: new THREE.Vector3( -100, 60, -0 ),
                // 			    // rotationMax: new THREE.Vector3( -100, 60, 0),
                //                 // minAngle:"1deg",
                //                 // minAngle:0.2,
                //                 // maxAngle:0.2
                //             },

                //             {
                //                 index: 32, //"mixamorig_RightArm" 手腕 hight    parent 是 shoulder
                //                 // rotationMin: new THREE.Vector3( 50, 0, -10 ),
                // 			    // rotationMax: new THREE.Vector3( 50, 60, 20),
                //                 // minAngle:0.8
                //                 // maxAngle:0.2
                //                 // minAngle:0.2,
                //                 // maxAngle:0.2
                //             },
                //             {
                //                 index: 31, //"mixamorig_RightArm" 手腕 hight    parent 是 shoulder
                //                 // rotationMin: new THREE.Vector3( 50, 0, -10 ),
                // 			    // rotationMax: new THREE.Vector3( 50, 60, 20),
                //                 // minAngle:0.8
                //                 // maxAngle:0.2
                //                 // minAngle:0.2,
                //                 // maxAngle:0.2
                //             },
                //         ],
                //     }
                // ];

                const iks = [
                    {
                        target: 38, // "mixamorig_Rightshoulder 最顶点"  4. 最外面的一根手指
                        effector: 37, // 拇指给一个 1  
                        links: [
                            {
                                // 链接的骨骼 low
                                index: 36, // "mixamorig_RightForeArm" 父  parent 是 RightArm
                                rotationMin: new THREE.Vector3(0, -0.1, -0),
                                rotationMax: new THREE.Vector3(0.2, 100, 0),
                            },
                            {
                                // 链接的骨骼 low
                                index: 35, // "mixamorig_RightForeArm" 父  parent 是 RightArm
                                rotationMin: new THREE.Vector3(1, 0, 0),
                                rotationMax: new THREE.Vector3(1, 10, 1),
                            },

                            // {
                            //     // 链接的骨骼 low
                            //     index: 31, // "mixamorig_RightForeArm" 父  parent 是 RightArm

                            // },
                            // {
                            //     // 链接的骨骼 low
                            //     index: 3, // "mixamorig_RightForeArm" 父  parent 是 RightArm

                            // },
                        ],
                    }
                ];

                // 第一个值传入蒙皮但是这个值好难控制
                console.log("nodeBody.hands", nodeBody.hands)
                // this.IKSolver = new CCDIKSolver( nodeBody.hands, iks );
                // this.IKSolver.update()   
                // const ccdikhelper = new CCDIKHelper( nodeBody.hands, iks, 0.003 );

                // this.scene.add( ccdikhelper );


            }

            initRender() {
                // this.initVisSprite()
                let v1 = new THREE.Vector3(0, 0, 0)
                // let renderer = new THREE.WebGLRenderer();
                this.renderer = new THREE.WebGLRenderer({ alpha: true });
                this.renderer.setSize(this.width, this.height);//设置渲染区域尺寸

                // 添加控制器
                let control = new OrbitControls(this.camera, this.renderer.domElement);
                control.target = v1 //设置相机方向(指向的场景对象)


                setTimeout(() => {
                    let transformControls = new TransformControls(this.camera, this.renderer.domElement);
                    transformControls.size = 1.15;
                    // transformControls.showX = false;
                    transformControls.space = 'world';
                    transformControls.attach(nodeBody.Head);


                    console.log("console.log(nodeBody.Head)")
                    console.log(nodeBody.Head)
                    transformControls.position.y = +10

                    this.scene.add(transformControls);
                    transformControls.addEventListener('mouseDown', () => control.enabled = false);
                    transformControls.addEventListener('mouseUp', () => control.enabled = true);

                }, 1000)


                // renderer.setClearColor(0xb9d3ff, 1);
                // 渲染函数
                let render = () => {

                    this.camera.updateProjectionMatrix();
                    // control.update();
                    // this.mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
                    this.renderer.render(this.scene, this.camera);//执行渲染操作
                    // mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
                    // console.log(nodeBody?.head)
                    try {
                        // nodeBody.head.position.y=nodeBody?.head?.position?.y+0.01
                        // nodeBody.head.position.x=nodeBody?.head?.position?.x+1
                        // angle= angle+0.000000001
                        // nodeBody.head.rotateY(angle)
                    } catch (e) {
                        console.log(e)
                    }

                    requestAnimationFrame(render);//请求再次执行渲染函数render，渲染下一帧
                }
                render();
                return this.renderer
            }


        }

        let three = new threeInit()
        three.initGui(1)






        // setInterval(() => {
        //     canvasMed(panel, param,`${Math.round(Math.random() * 60)}M/s`);
        //     dynamicSprite.material.map.needsUpdate = true;
        // }, 1000);


        // 重要。添加骨架
        // three.scene.add(skeleton)
        // three.initSkyBox()
        document.querySelector('.canvas').appendChild(three.initRender().domElement);





        document.querySelector("button").addEventListener('click', () => {
            main(three.mesh, nodeBody,nodeBody.Head.position.y)

        })

    </script>





    <script defer>

        function main(model, nodeBody,height) {

            console.log(model, nodeBody)
            let loganHandle = {
                prev: {
                    x: null,
                    y: null
                },
                next: {
                    x: null,
                    y: null
                }
            }
            let distance = {
                x_distance: null,
                y_distance: null,
                allDistance: null
            }

            // 求x y 移动的距离
            let loganHandleFn = (val) => {
                val.x = val.x * 100
                val.y = val.y * 100
                // 一开始的数据
                if (!loganHandle.prev.x && !loganHandle.next.x) {
                    loganHandle.prev = JSON.parse(JSON.stringify(val))
                    return
                }
                // 之后的数据
                if (loganHandle.prev.x && !loganHandle.next.x) {
                    loganHandle.next = JSON.parse(JSON.stringify(loganHandle.prev))
                    loganHandle.prev = JSON.parse(JSON.stringify(val))
                }
                // 更新数据
                if (loganHandle.prev.x && loganHandle.next.x) {
                    loganHandle.prev = JSON.parse(JSON.stringify(loganHandle.next))
                    loganHandle.next = JSON.parse(JSON.stringify(val))
                }
                if (loganHandle.prev.x && loganHandle.next.x) {
                    let x_distance = Math.pow((loganHandle.prev.x - loganHandle.next.x), 2)
                    let y_distance = Math.pow((loganHandle.prev.y - loganHandle.next.y), 2)
                    // console.log("笛卡尔距离：",x_distance,y_distance,Math.log2(x_distance+y_distance))
                    if (Math.log2(x_distance + y_distance)) {
                        // console.log("笛卡尔距离：",x_distance,y_distance,Math.log2(x_distance+y_distance))
                        distance.allDistance = Math.log2(x_distance + y_distance)

                    }
                    distance.x_distance = Math.log2(x_distance)
                    distance.y_distance = Math.log2(y_distance)
                }
            }


            const videoElement = document.getElementsByClassName('input_video')[0];

            function onResults(results) {
                // console.log(results?.poseLandmarks[16])
                try {
                    let thumb = results?.leftHandLandmarks[4]
                    let secondThumb = results?.leftHandLandmarks[8]
                    let distance_tempX = Math.pow((thumb.x - secondThumb.x), 2)
                    let distance_tempY = Math.pow((thumb.y - secondThumb.y), 2)
                    let distance_tempZ = Math.pow((thumb.z - secondThumb.z), 2)
                    let distance_thumb = Math.pow((distance_tempX + distance_tempY + distance_tempZ), 0.5)
                    // console.log(distance)


                    let scale = distance_thumb
                    // 实现缩放
                    // model.scale.set(scale * 6, scale * 6, scale * 6);


                    loganHandleFn({
                        x: thumb.x,
                        y: thumb.y
                    })
                    // 实现旋转
                    // 实现平移

                    // console.log(distance, loganHandle)
                    console.log(results?.poseLandmarks[0])
                    if (distance.x_distance > 0.1) {
                        // console.log("横着走")
                        // speakbot.speak("横着走")
                        // window.scrollBy(document.body.clientWidth * (loganHandle.prev.x - loganHandle.next.x) / 1000, 0)
                        // model.position.z = model.position.z + (loganHandle.prev.x - loganHandle.next.x) /2
                        // model.rotateY( (loganHandle.prev.x - loganHandle.next.x) /10);
                        // nodeBody.Head = 
                        // nodeBody.Head = 
                        
                    }
                    if (distance.y_distance > 0.1) {
                        // console.log("竖着走")
                        // speakbot.speak("竖着走")
                        // model.position.y = model.position.y + (loganHandle.prev.y - loganHandle.next.y) *4
                        // window.scrollBy(0, document.body.clientHeight * (loganHandle.prev.y - loganHandle.next.y) / 1000)
                        // nodeBody.Head = 
                        
                    }
                    
                    nodeBody.Head.position.x =0+ 30*(results?.poseLandmarks[0].x-0.5)
                    nodeBody.Head.position.y =height -15*(results?.poseLandmarks[0].y)+10
                } catch {

                }

            }

            const holistic = new Holistic({
                locateFile: (file) => {
                    console.log(file, "file")
                    return `https://cdn.jsdelivr.net/npm/electroluxasset@1.0.5/mediapipe/holistic/locate/${file}`;
                }
            });
            holistic.setOptions({
                modelComplexity: 1,
                smoothLandmarks: true,
                enableSegmentation: true,
                smoothSegmentation: true,
                refineFaceLandmarks: true,
                minDetectionConfidence: 0.5,
                minTrackingConfidence: 0.5
            });
            holistic.onResults(onResults);

            const camera = new Camera(videoElement, {
                onFrame: async () => {
                    await holistic.send({ image: videoElement });
                },
                width: 200,
                height: 190
            });

            camera.start();


            // if (Math.log2(diff_x_distance + diff_y_distance)) {

            //     diff_distance = (Math.log2(diff_x_distance + diff_y_distance))/50
            // }
            // if(diff_distance>0.6 || diff_distance<0.5){
            //     model.scale.set(diff_distance,diff_distance, diff_distance);
            // }


        }




    // main()
    </script>
</body>

</html>